package com.wn.system.service.impl;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wn.access.api.BankAccessApi;
import com.wn.common.constant.redis.RedisConstant;
import com.wn.common.exception.Assert;
import com.wn.common.jwt.JwtTemplate;
import com.wn.common.model.dto.bank.BankCardAddDTO;
import com.wn.common.model.dto.bank.CardAddDTO;
import com.wn.common.model.dto.user.LoginDto;
import com.wn.common.model.dto.user.UserRegisterDto;
import com.wn.common.model.vo.bank.SignatureVo;
import com.wn.common.model.vo.user.LoginVo;
import com.wn.common.model.vo.user.TokenVo;
import com.wn.common.model.vo.user.UserInfoVo;
import com.wn.common.result.Result;
import com.wn.common.result.enums.impl.BusinessCode;
import com.wn.common.result.enums.impl.ResultCode;
import com.wn.common.utils.GeneratorCodeUtil;
import com.wn.common.utils.TokenUtils;
import com.wn.system.entity.User;
import com.wn.system.entity.UserCard;
import com.wn.system.entity.UserDetail;
import com.wn.system.mapper.UserMapper;
import com.wn.system.service.IUserCardService;
import com.wn.system.service.IUserDetailService;
import com.wn.system.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author yellow docker
 * @since 2023-02-23
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private IUserDetailService userDetailService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private JwtTemplate jwtTemplate;

    @Autowired
    private IUserCardService userCardService;

    @Autowired
    private BankAccessApi bankAccessApi;

    @Transactional
    @Override
    public void register(UserRegisterDto userRegisterDto) {

        //判断 这个手机号是否重复
        Long count = checkPhoneUser(userRegisterDto.getPhone());

        //抛出异常
        Assert.error(count > 0 , BusinessCode.USER_EXISTED);
        
        // 01 添加一个 user
        User user = new User();
        BeanUtils.copyProperties(userRegisterDto,user);
        user.setUserType("1");
        user.setUserStatus(true);
        //进行加密操作  passwordEncoder 加密工具
        user.setPwd(passwordEncoder.encode(userRegisterDto.getPwd()));

        save(user);

        // 02 添加一个 userDetail
        UserDetail userDetail = new UserDetail();
        BeanUtils.copyProperties(userRegisterDto,userDetail);
        userDetail.setId(user.getId());
        userDetail.setUserCode(UUID.randomUUID().toString());
        userDetail.setBorrowMoney(new BigDecimal("0"));
        userDetail.setCardBindStatus(false);

        userDetailService.save(userDetail);
    }


    /**
     * 登录操作的业务实现
     * @param loginDto
     * @return
     */
    @Override
    public LoginVo login(LoginDto loginDto) {

        //1、判断是否为手机登录
        Boolean isPhoneLogin = loginDto.getIsPhoneLogin();
        if (isPhoneLogin) {
            return smsLogin(loginDto);
        }
        return userAndPwdLogin(loginDto);
    }

    /**
     * 绑卡的业务
     * @param cardAddDTO
     */
    @Override
    public  Result<SignatureVo> createConsumer(CardAddDTO cardAddDTO) {

        //01 获取token 中的登录信息
        TokenVo tokenVo = TokenUtils.getTokenVo();
        System.out.println("tokenVo = " + tokenVo);

        //给 userCard表中添加银行卡绑定信息
        //1、检查是否已经关联银行卡 （判断  u_user_detail 的 _card_bind_status 状态）
        UserDetail userDetail = userDetailService.getOne(Wrappers.lambdaQuery(UserDetail.class)
                .eq(UserDetail::getPhone,tokenVo.getPhone()));

        //绑定了 就是 true 没有false
        // false = userDetail.getCardBindStatus() =  0 (没有绑卡 )
        Assert.error(userDetail.getCardBindStatus(), BusinessCode.USER_CARD_ALREADY_BINDED);

        //2、是否已经绑定银行卡  (判断  u_card 的 是否有数据  )
        UserCard userCard = userCardService.getOne(Wrappers.lambdaQuery(UserCard.class).eq(UserCard::getPhone, tokenVo.getPhone()));

        //userCard != null (数据已存在  不能绑卡)
        Assert.error(userCard != null, BusinessCode.USER_CARD_ALREADY_BINDED);
        // 3、更新用户信息    (u_user  更新：real_name\id\phone )
        update(Wrappers.lambdaUpdate(User.class)
                .set(User::getRealName,cardAddDTO.getRealName())
                .set(User::getIdentityCode,cardAddDTO.getIdentityCode())
                .eq(User::getPhone,tokenVo.getPhone())
        );

        //银行用户卡信息
        UserCard newCard = new UserCard();
        newCard.setUserId(Long.parseLong(tokenVo.getUserId()));
        newCard.setCardNo(cardAddDTO.getCardNo());
        newCard.setPhone(tokenVo.getPhone());
        newCard.setStatus(false);

        userCardService.save(newCard);
        log.info("绑卡数据添加到db中");
        log.info("发送给银行托管平台进行日志记录");

        //        5、发送数据到银行托管代理服务  调用托管机构
        BankCardAddDTO bankCardAddDTO = new BankCardAddDTO();
        //流水码
        bankCardAddDTO.setReqCode(GeneratorCodeUtil.generator("ADD"));
        //用户id
        bankCardAddDTO.setUserId(Long.parseLong(tokenVo.getUserId()));

        Result<SignatureVo> result = bankAccessApi.addBankAccount(bankCardAddDTO);

        return result;
    }

    @Override
    public UserInfoVo findUserById(String phone) {

        User user = getOne(Wrappers.lambdaQuery(User.class).eq(User::getPhone, phone));

        //查询 user表
        UserDetail userDetail = userDetailService.getOne(Wrappers.lambdaQuery(UserDetail.class).eq(UserDetail::getPhone, phone));
        //查询 userDetail标签

        UserInfoVo userInfoVo = new UserInfoVo();
        userInfoVo.setUserName(userDetail.getUserName());
        userInfoVo.setUserCode(userDetail.getUserCode());
        userInfoVo.setIdentityCode(user.getIdentityCode());

        return userInfoVo;
    }

    /**
     * 短信登录
     *      1.1 先访问《发送验证码接口》，发送短信验证码
     *      1.2 校验验证码: loginSecret 与 Redis中验证码是否一致
     *      1.3 如果验证码输入正确，再查询手机号码在数据库是否存在
     *          不存在：自动注册，保存用户、用户明细
     *              用户名：随机生成
     *              密码：默认123456
     *              对于用户名与密码，登陆后可以修改
     *          存在：返回
     * @param loginDto
     * @return
     */
    private LoginVo smsLogin(LoginDto loginDto) {
        String phone = loginDto.getLoginName();
        //01 校验验证码: loginSecret 与 Redis中验证码是否一致
        String loginSecret = loginDto.getLoginSecret();
        String redisCode = stringRedisTemplate.opsForValue().get(RedisConstant.SMS_PREFIX + phone);
        //判断 code是否存在
        Assert.error(redisCode,BusinessCode.USER_VERIFY_CODE_OVER);  //不存在抛出异常
        //判断 code 是否一致
        Assert.error(!redisCode.equals(loginSecret),BusinessCode.USER_VERIFY_CODE_ERROR);//不一致抛出异常
//        1.3 如果验证码输入正确，再查询手机号码在数据库是否存在
        User user = findUserByPhone(phone);
        if (user ==null){

            //不存在 进行注册
            UserRegisterDto registerDto = new UserRegisterDto();
            registerDto.setPhone(phone);
            registerDto.setPwd("123456");
            registerDto.setUserName(RandomUtil.randomString(6));

            //不存在 进行注册
            register(registerDto);

            //查询一下
            user = findUserByPhone(phone);
        }
        //user 转成 loginVo
        LoginVo vo = new LoginVo();
        BeanUtils.copyProperties(user,vo);
        //创建一个token
        Map<String,Object> payload = new HashMap<>();
        payload.put("userId",user.getId());
        payload.put("phone",user.getPhone());
        String token = jwtTemplate.createToken(payload);
        //存储到redis 中
        stringRedisTemplate.opsForValue().set(RedisConstant.USER_REDIS_TOKEN + user.getId(),token,10, TimeUnit.DAYS);
        //token 回写
        vo.setToken(token);
        return vo;
    }

    /**
     * 查询user
     * @param phone
     * @return
     */
    private User findUserByPhone(String phone) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class).eq(User::getPhone, phone);
        return getOne(wrapper);
    }


    private LoginVo userAndPwdLogin(LoginDto loginDto) {
        //大家自己写
        return null;
    }

    /**
     * 通过手机号码判断用户是否存在
     * @param phone
     * @return
     */
    private Long checkPhoneUser(String phone) {
        //条件 设置
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(User::getPhone,phone);

        return count(wrapper);
    }
}